// Created by Windward Studios - no copyright is claimed. This code can be used in
// any manner by anyone for any reason. There is no copyright of any kind on it. You may
// use it in commercial products. You may change it without sharing those changes.
// We ask that you keep the "created by Windward Studios" in a comment at the top.

using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Web.Configuration;

namespace net.windward.utils.ado
{
	/// <summary>
	/// Wrapper around DbConnection. Uses DbProviderFactories to create connections and commands.
	/// </summary>
	public class WrConnection : IDbConnection
	{

		private readonly DbProviderFactory provider;
		private readonly DbConnection conn;

		/// <summary>
		/// Create a connection. This also assigns the connection string.
		/// </summary>
		/// <param name="connStrName">The name of a connection string in the config file.</param>
		public WrConnection(string connStrName)
		{

			ConnectionStringSettings connStr = WebConfigurationManager.ConnectionStrings[connStrName];
			if (connStr == null)
				throw new WrDbException("No connection string for " + connStrName);
			provider = DbProviderFactories.GetFactory(connStr.ProviderName);

			conn = provider.CreateConnection();
			conn.ConnectionString = connStr.ConnectionString;
		}

		/// <summary>
		/// Create a connection. There is no connection string assigned.
		/// </summary>
		/// <param name="provider">The name of a connection string in the config file.</param>
		public WrConnection(DbProviderFactory provider)
		{
			this.provider = provider;
			conn = provider.CreateConnection();
		}

		///<summary>
		///Creates and returns a Command object associated with the connection.
		///</summary>
		///
		///<returns>
		///A Command object associated with the connection.
		///</returns>
		///<filterpriority>2</filterpriority>
		public IDbCommand CreateCommand()
		{

			string odbcProvider = GetOdbcProvider(provider.GetType().FullName, conn.ConnectionString);

			WrCommand cmd = WrCommand.Factory(provider, odbcProvider);
			cmd.Connection = conn;
			return cmd;
		}

		///<summary>
		/// Get the ODBC or OleDB provider out of the connection string.
		///</summary>
		///<param name="providerName">The provider for this connection.</param>
		///<param name="connectionString">The connection string.</param>
		///<returns>The provider. null if none.</returns>
		public static string GetOdbcProvider(string providerName, string connectionString)
		{
			string prov = WrProviderFactories.DetermineProvider(providerName);
			if (prov == "ODBC")
			{
				int pos = connectionString.ToLower().IndexOf("driver=");
				if (pos != -1)
				{
					connectionString = connectionString.Substring(pos + 7);
					pos = connectionString.IndexOf(';');
					if (pos != -1)
						connectionString = connectionString.Substring(0, pos);
					string odbcProvider = connectionString.Trim();
					if (odbcProvider.StartsWith("{"))
						odbcProvider = odbcProvider.Substring(1).Trim();
					if (odbcProvider.EndsWith("}"))
						odbcProvider = odbcProvider.Substring(0, odbcProvider.Length - 1).Trim();
					return odbcProvider;
				}
			}
			if (prov == "OleDb")
			{
				int pos = connectionString.ToLower().IndexOf("provider=");
				if (pos != -1)
				{
					connectionString = connectionString.Substring(pos + 9);
					pos = connectionString.IndexOf(';');
					if (pos != -1)
						connectionString = connectionString.Substring(0, pos);
					return connectionString.Trim();
				}
			}

			return null;
		}

		///<summary>
		///Creates and returns a Command object associated with the connection.
		///</summary>
		///
		///<returns>
		///A Command object associated with the connection.
		///</returns>
		///<filterpriority>2</filterpriority>
		public WrCommand CreateWrCommand()
		{
			return (WrCommand) CreateCommand();
		}

		#region stock overrides

		///<summary>
		///Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		///</summary>
		///<filterpriority>2</filterpriority>
		public void Dispose()
		{
			if (conn != null)
				conn.Dispose();
		}

		///<summary>
		///Begins a database transaction.
		///</summary>
		///
		///<returns>
		///An object representing the new transaction.
		///</returns>
		///<filterpriority>2</filterpriority>
		public IDbTransaction BeginTransaction()
		{
			return conn.BeginTransaction();
		}

		///<summary>
		///Begins a database transaction with the specified value.
		///</summary>
		///
		///<returns>
		///An object representing the new transaction.
		///</returns>
		///
		///<param name="il">One of the values. </param>
		/// <filterpriority>2</filterpriority>
		public IDbTransaction BeginTransaction(IsolationLevel il)
		{
			return conn.BeginTransaction(il);
		}

		///<summary>
		///Closes the connection to the database.
		///</summary>
		///<filterpriority>2</filterpriority>
		public void Close()
		{
			conn.Close();
		}

		///<summary>
		///Changes the current database for an open Connection object.
		///</summary>
		///
		///<param name="databaseName">The name of the database to use in place of the current database. </param><filterpriority>2</filterpriority>
		public void ChangeDatabase(string databaseName)
		{
			conn.ChangeDatabase(databaseName);
		}

		///<summary>
		///Opens a database connection with the settings specified by the ConnectionString property of the provider-specific Connection object.
		///</summary>
		///<filterpriority>2</filterpriority>
		public void Open()
		{
			conn.Open();
		}

		///<summary>
		///Gets or sets the string used to open a database.
		///</summary>
		///
		///<returns>
		///A string containing connection settings.
		///</returns>
		///<filterpriority>2</filterpriority>
		public string ConnectionString
		{
			get { return conn.ConnectionString; }
			set { conn.ConnectionString = value; }
		}

		///<summary>
		///Gets the time to wait while trying to establish a connection before terminating the attempt and generating an error.
		///</summary>
		///
		///<returns>
		///The time (in seconds) to wait for a connection to open. The default value is 15 seconds.
		///</returns>
		///<filterpriority>2</filterpriority>
		public int ConnectionTimeout
		{
			get { return conn.ConnectionTimeout; }
		}

		///<summary>
		///Gets the name of the current database or the database to be used after a connection is opened.
		///</summary>
		///
		///<returns>
		///The name of the current database or the name of the database to be used once a connection is open. The default value is an empty string.
		///</returns>
		///<filterpriority>2</filterpriority>
		public string Database
		{
			get { return conn.Database; }
		}

		///<summary>
		///Gets the current state of the connection.
		///</summary>
		///
		///<returns>
		///One of the <see cref="T:System.Data.ConnectionState"></see> values.
		///</returns>
		///<filterpriority>2</filterpriority>
		public ConnectionState State
		{
			get { return conn.State; }
		}

		#endregion
	}
}
